#include <sdcc-lib.h>
#include <malloc.h>
#include <string.h>

#if defined(__SDCC_STACK_AUTO) || defined(__SDCC_z80) || defined(__SDCC_z180) || defined(__SDCC_gbz80)
#define CRITICAL __critical
#else
#define CRITICAL
#endif

//--------------------------------------------------------------------
//realloc function implementation for embedded system
//Non-ANSI keywords are C51 specific.
// __xdata - variable in external memory (just RAM)
//--------------------------------------------------------------------

#if _SDCC_MALLOC_TYPE_MLH

#define __xdata

typedef struct _MEMHEADER MEMHEADER;

struct _MEMHEADER {
  MEMHEADER *   next;
  MEMHEADER *   prev;
  unsigned int  len;
  unsigned char mem;
};

#define HEADER_SIZE (sizeof(MEMHEADER)-sizeof(char))
#define MEM(x)      (&x->mem)

#else

#define MEMHEADER   struct MAH// Memory Allocation Header

MEMHEADER {
  MEMHEADER __xdata *  next;
  unsigned int         len;
  unsigned char        mem[];
};

#define HEADER_SIZE sizeof(MEMHEADER)
#define MEM(x)      (x->mem)

#endif

extern MEMHEADER __xdata * _sdcc_prev_memheader;

// apart from finding the header
// this function also finds it's predecessor
extern MEMHEADER __xdata * _sdcc_find_memheader( void __xdata * p );

void __xdata * realloc( void * p, size_t size ) {
  register MEMHEADER __xdata * pthis;
  register MEMHEADER __xdata * pnew;
  register void __xdata * ret;
  CRITICAL {
    pthis = _sdcc_find_memheader( p );
    if( pthis ) {
      if( size > ( 0xFFFF - HEADER_SIZE ) ) {
        ret = ( void __xdata * ) NULL; //To prevent overflow in next line
      } else {
        size += HEADER_SIZE; //We need a memory for header too
        if( ( ( ( unsigned int )pthis->next ) - ( ( unsigned int )pthis ) ) >= size ) {
          //if spare is more than needed
          pthis->len = size;
          ret = p;
        } else {
          if( ( _sdcc_prev_memheader ) &&
              ( ( ( ( unsigned int )pthis->next ) -
                  ( ( unsigned int )_sdcc_prev_memheader ) -
                  _sdcc_prev_memheader->len ) >= size ) ) {
            pnew = ( MEMHEADER __xdata * )( ( char __xdata * )_sdcc_prev_memheader + _sdcc_prev_memheader->len );
            _sdcc_prev_memheader->next = pnew;
            #if _SDCC_MALLOC_TYPE_MLH
            pthis->next->prev = pnew;
            #endif
            memmove( pnew, pthis, pthis->len );
            pnew->len = size;
            ret = MEM( pnew );
          } else {
            ret = malloc( size - HEADER_SIZE );
            if( ret ) {
              memcpy( ret, MEM( pthis ), pthis->len - HEADER_SIZE );
              free( p );
            }
          }
        }
      }
    } else {
      ret = malloc( size );
    }
  }
  return ret;
}
//END OF MODULE
